package slicegen;

import java.awt.geom.Point2D;
import java.util.ArrayList;
import java.util.ListIterator;

public class LineSet
{
	public LineSet(Line line)
	{
		points = new ArrayList<Point2D>();

		points.add(line.getP1());
		points.add(line.getP2());
	}

	//TODO also look for areas in the middle of the segment to insert into
	public boolean merge(LineSet line)
	{
		if (line.equals(this))
			return false;

		Point2D p1 = line.getStart(), p2 = line.getEnd();
		ArrayList<Point2D> linePts = line.points;

		boolean added = false;

		if (comparePoints(p1, getStart()))
		{
			//points.add(0, p2);
			//New points must be added to front of list in reverse order to maintain shape
			mergePoints(line, false, linePts.listIterator(linePts.size()), 0);
			// System.out.print("R E 0");
			added = true;
		}
		else if (comparePoints(p2, getStart()))
		{
			//points.add(0, p1);
			//New points must be added to front of list in forward order
			mergePoints(line, true, linePts.listIterator(), 0);
			// System.out.print("F B 0");
			added = true;
		}
		else if (comparePoints(p1, getEnd()))
		{
			//points.add(p2);
			//New points must be added to rear of list in forward order 
			mergePoints(line, true, linePts.listIterator(1), points.size());
			//  System.out.print("F B E");
			added = true;
		}
		else if (comparePoints(p2, getEnd()))
		{
			//New points must be added to rear of list in reverse order
			//points.add(p1);
			mergePoints(line, false, linePts.listIterator(linePts.size()), points.size());
			//  System.out.print("R E E");
			added = true;
		}

		return added;
	}

	/**
	 * 
	 * @param set	LineSet to add points to
	 * @param forward   Add from start->end (true) or end->start (false)
	 * @param beginIndex Index to start adding points from
	 * @param insertIndex Index to start inserting into
	 */
	private void mergePoints(LineSet set, boolean forward, ListIterator<Point2D> iter, int insertIndex)
	{
		//Insert points[index+1] iter.start()->newpoints.end points[index+2]
		if (forward)
		{
			while (iter.hasNext())
			{
				points.add(insertIndex++, iter.next());
			}
		}
		//Insert points[index+1] points.start<-iter.start points[index+2]	
		else
		{
			while (iter.hasPrevious())
			{
				points.add(insertIndex++, iter.previous());
			}
		}
	}
	
	public void simplifyPoints()
	{
		System.out.println("Points before: " + points.size());
		
		for (int i = 0; i < points.size() - 2; i++)
		{
			double a = points.get(i).distanceSq(points.get(i+1));
			double b = points.get(i+1).distanceSq(points.get(i+2));
			double c = points.get(i).distanceSq(points.get(i+2));
			
			double angle = Math.toDegrees(Math.acos((c - a - b)/(-2*Math.sqrt(a)*Math.sqrt(b))));
			
		//	System.out.println(angle);
			
			if (Double.isNaN(angle))
			{
				System.out.println("Removed a point");				
				points.remove(i+1);				
				i=0;
			}
		}
		
		System.out.println("Points after: " + points.size());
	}

	public Point2D getStart()
	{
		return points.get(0);
	}

	public Point2D getEnd()
	{
		return points.get(points.size() - 1);
	}

	public ArrayList<Point2D> getPoints()
	{
		return points;
	}

	public int numVertices()
	{
		return points.size();
	}

	public double getLength()
	{
		double length = 0;

		Point2D prev = getStart();

		for (int i = 1; i < points.size(); i++)
		{
			length += prev.distance(points.get(i));
			prev = points.get(i);
		}

		return length;
	}

	private boolean comparePoints(Point2D p1, Point2D p2)
	{
		return (Math.abs(p1.getX() - p2.getX()) < TOL && Math.abs(p1.getY() - p2.getY()) < TOL);
	}

	@Override
	public String toString()
	{
		return getStart() + " " + getEnd() + " " + getLength();
	}

	/**
	 * 
	 * @return true if the tolerance could be increased
	 */
	public static boolean increaseTolerance()
	{
		tolIndex++;

		if (tolIndex >= TOLS.length)
			return false;
		else
			TOL = TOLS[tolIndex];

		return true;
	}

	public static void resetTolerance()
	{
		tolIndex = 0;
		TOL = TOLS[tolIndex];
	}

	public static double getTolerance()
	{
		return TOL;
	}

	private static final double[] TOLS = {1E-8, 1E-7, 1E-6, 1E-5, 1E-4, 1E-3};
	private static int tolIndex = 0;
	protected static double TOL = TOLS[tolIndex];

	protected ArrayList<Point2D> points;

	public boolean contains(Point2D point2D)
	{
		for (Point2D pt : points)
		{
			if (comparePoints(pt, point2D))
				return true;
		}
		return false;
	}
}
